/*-------------------------------------------------------------------------
 *
 * gindesc.c
 *	  rmgr 描述例程，用于 access/transam/gin/ginxlog.c
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/access/rmgrdesc/gindesc.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/ginxlog.h"
#include "access/xlogutils.h"
#include "lib/stringinfo.h"
#include "storage/relfilenode.h"

static void fc_desc_recompress_leaf(StringInfo fc_buf, ginxlogRecompressDataLeaf *fc_insertData)
{
	int			fc_i;
	char	   *fc_walbuf = ((char *) fc_insertData) + sizeof(ginxlogRecompressDataLeaf);

	appendStringInfo(fc_buf, " %d segments:", (int) fc_insertData->nactions);

	for (fc_i = 0; fc_i < fc_insertData->nactions; fc_i++)
	{
		uint8		fc_a_segno = *((uint8 *) (fc_walbuf++));
		uint8		fc_a_action = *((uint8 *) (fc_walbuf++));
		uint16		fc_nitems = 0;
		int			fc_newsegsize = 0;

		if (fc_a_action == GIN_SEGMENT_INSERT ||
			fc_a_action == GIN_SEGMENT_REPLACE)
		{
			fc_newsegsize = SizeOfGinPostingList((GinPostingList *) fc_walbuf);
			fc_walbuf += SHORTALIGN(fc_newsegsize);
		}

		if (fc_a_action == GIN_SEGMENT_ADDITEMS)
		{
			memcpy(&fc_nitems, fc_walbuf, sizeof(uint16));
			fc_walbuf += sizeof(uint16);
			fc_walbuf += fc_nitems * sizeof(ItemPointerData);
		}

		switch (fc_a_action)
		{
			case GIN_SEGMENT_ADDITEMS:
				appendStringInfo(fc_buf, " %d (add %d items)", fc_a_segno, fc_nitems);
				break;
			case GIN_SEGMENT_DELETE:
				appendStringInfo(fc_buf, " %d (delete)", fc_a_segno);
				break;
			case GIN_SEGMENT_INSERT:
				appendStringInfo(fc_buf, " %d (insert)", fc_a_segno);
				break;
			case GIN_SEGMENT_REPLACE:
				appendStringInfo(fc_buf, " %d (replace)", fc_a_segno);
				break;
			default:
				appendStringInfo(fc_buf, " %d unknown action %d ???", fc_a_segno, fc_a_action);
				/* 无法解码未识别的操作 */
				return;
		}
	}
}

void gin_desc(StringInfo fc_buf, XLogReaderState *fc_record)
{
	char	   *fc_rec = XLogRecGetData(fc_record);
	uint8		fc_info = XLogRecGetInfo(fc_record) & ~XLR_INFO_MASK;

	switch (fc_info)
	{
		case XLOG_GIN_CREATE_PTREE:
			/* 没有更多信息 */
			break;
		case XLOG_GIN_INSERT:
			{
				ginxlogInsert *fc_xlrec = (ginxlogInsert *) fc_rec;

				appendStringInfo(fc_buf, "isdata: %c isleaf: %c",
								 (fc_xlrec->flags & GIN_INSERT_ISDATA) ? 'T' : 'F',
								 (fc_xlrec->flags & GIN_INSERT_ISLEAF) ? 'T' : 'F');
				if (!(fc_xlrec->flags & GIN_INSERT_ISLEAF))
				{
					char	   *fc_payload = fc_rec + sizeof(ginxlogInsert);
					BlockNumber fc_leftChildBlkno;
					BlockNumber fc_rightChildBlkno;

					fc_leftChildBlkno = BlockIdGetBlockNumber((BlockId) fc_payload);
					fc_payload += sizeof(BlockIdData);
					fc_rightChildBlkno = BlockIdGetBlockNumber((BlockId) fc_payload);
					fc_payload += sizeof(BlockNumber);
					appendStringInfo(fc_buf, " children: %u/%u",
									 fc_leftChildBlkno, fc_rightChildBlkno);
				}
				if (XLogRecHasBlockImage(fc_record, 0))
				{
					if (XLogRecBlockImageApply(fc_record, 0))
						appendStringInfoString(fc_buf, " (full page image)");
					else
						appendStringInfoString(fc_buf, " (full page image, for WAL verification)");
				}
				else
				{
					char	   *fc_payload = XLogRecGetBlockData(fc_record, 0, NULL);

					if (!(fc_xlrec->flags & GIN_INSERT_ISDATA))
						appendStringInfo(fc_buf, " isdelete: %c",
										 (((ginxlogInsertEntry *) fc_payload)->isDelete) ? 'T' : 'F');
					else if (fc_xlrec->flags & GIN_INSERT_ISLEAF)
						fc_desc_recompress_leaf(fc_buf, (ginxlogRecompressDataLeaf *) fc_payload);
					else
					{
						ginxlogInsertDataInternal *fc_insertData =
						(ginxlogInsertDataInternal *) fc_payload;

						appendStringInfo(fc_buf, " pitem: %u-%u/%u",
										 PostingItemGetBlockNumber(&fc_insertData->newitem),
										 ItemPointerGetBlockNumber(&fc_insertData->newitem.key),
										 ItemPointerGetOffsetNumber(&fc_insertData->newitem.key));
					}
				}
			}
			break;
		case XLOG_GIN_SPLIT:
			{
				ginxlogSplit *fc_xlrec = (ginxlogSplit *) fc_rec;

				appendStringInfo(fc_buf, "isrootsplit: %c",
								 (((ginxlogSplit *) fc_rec)->flags & GIN_SPLIT_ROOT) ? 'T' : 'F');
				appendStringInfo(fc_buf, " isdata: %c isleaf: %c",
								 (fc_xlrec->flags & GIN_INSERT_ISDATA) ? 'T' : 'F',
								 (fc_xlrec->flags & GIN_INSERT_ISLEAF) ? 'T' : 'F');
			}
			break;
		case XLOG_GIN_VACUUM_PAGE:
			/* 没有更多信息 */
			break;
		case XLOG_GIN_VACUUM_DATA_LEAF_PAGE:
			{
				if (XLogRecHasBlockImage(fc_record, 0))
				{
					if (XLogRecBlockImageApply(fc_record, 0))
						appendStringInfoString(fc_buf, " (full page image)");
					else
						appendStringInfoString(fc_buf, " (full page image, for WAL verification)");
				}
				else
				{
					ginxlogVacuumDataLeafPage *fc_xlrec =
					(ginxlogVacuumDataLeafPage *) XLogRecGetBlockData(fc_record, 0, NULL);

					fc_desc_recompress_leaf(fc_buf, &fc_xlrec->data);
				}
			}
			break;
		case XLOG_GIN_DELETE_PAGE:
			/* 没有更多信息 */
			break;
		case XLOG_GIN_UPDATE_META_PAGE:
			/* 没有更多信息 */
			break;
		case XLOG_GIN_INSERT_LISTPAGE:
			/* 没有更多信息 */
			break;
		case XLOG_GIN_DELETE_LISTPAGE:
			appendStringInfo(fc_buf, "ndeleted: %d",
							 ((ginxlogDeleteListPages *) fc_rec)->ndeleted);
			break;
	}
}

const char * gin_identify(uint8 fc_info)
{
	const char *fc_id = NULL;

	switch (fc_info & ~XLR_INFO_MASK)
	{
		case XLOG_GIN_CREATE_PTREE:
			fc_id = "CREATE_PTREE";
			break;
		case XLOG_GIN_INSERT:
			fc_id = "INSERT";
			break;
		case XLOG_GIN_SPLIT:
			fc_id = "SPLIT";
			break;
		case XLOG_GIN_VACUUM_PAGE:
			fc_id = "VACUUM_PAGE";
			break;
		case XLOG_GIN_VACUUM_DATA_LEAF_PAGE:
			fc_id = "VACUUM_DATA_LEAF_PAGE";
			break;
		case XLOG_GIN_DELETE_PAGE:
			fc_id = "DELETE_PAGE";
			break;
		case XLOG_GIN_UPDATE_META_PAGE:
			fc_id = "UPDATE_META_PAGE";
			break;
		case XLOG_GIN_INSERT_LISTPAGE:
			fc_id = "INSERT_LISTPAGE";
			break;
		case XLOG_GIN_DELETE_LISTPAGE:
			fc_id = "DELETE_LISTPAGE";
			break;
	}

	return fc_id;
}
